﻿#include "NrfjprogCMDMgr.h"
#include "WinCMDUtil.h"
#include <QDebug>

#include <QEventLoop>
#include <QTimer>




NrfjprogCMDMgr * NrfjprogCMDMgr::m_instance = nullptr;

NrfjprogCMDMgr::NrfjprogCMDMgr()
    : cmd_("nrfjprog")
    , sn_config_address(PAGE_ADDR)
    , sn_config_rule_len(SN_MAX_LEN)
    , sn_config_mem_len(16)
{

}

NrfjprogCMDMgr* NrfjprogCMDMgr::getInstance()
{
	if (!m_instance)
	{
        m_instance = new NrfjprogCMDMgr();
	}
	return m_instance;
}

int32_t NrfjprogCMDMgr::SetSNConfig(zl::SNConfig config)
{
    bool isOK;
    int32_t value = config.address.toLower().toInt(&isOK, 16);

    if (!isOK || config.sn_mem_bytes_len < config.sn_rule_len) return ENCMD_Result_Failed;

    sn_config_address = value;
    sn_config_rule_len = config.sn_rule_len;
    sn_config_mem_len = config.sn_mem_bytes_len;

    return ENCMD_Result_Success;
}

int32_t NrfjprogCMDMgr::NrfjprogEraseMcu()
{
    qDebug() << "[INFO]" << "nrfjprog erase mcu cmd";

    QStringList parameters("--eraseall");

    QString ret = WinCMDUtil::doCmd(cmd_, parameters);

    qDebug() << "nrfjprog erase all cmd ret info: "<< ret;

    if (ret.endsWith("Applying system reset.\r\n")) return ENCMD_Result_Success;

    return ENCMD_Result_Failed;
}

int32_t NrfjprogCMDMgr::NrfjprogEraseSNMemory()
{
    qDebug() << "[INFO]" << "nrfjprog erase sn memory";

    QString addr = "0x" + QString::number(sn_config_address, 16);

    qDebug() << "[INFO]" << "page address: " << addr;

    QStringList parameters("--erasepage");
    parameters << addr;

    QString ret = WinCMDUtil::doCmd(cmd_, parameters);

    qDebug() << "nrfjprog erase page cmd ret info: "<< ret;

    if (ret.contains("Initializing the QSPI peripheral.") && ret.contains("Uninitializing the QSPI peripheral.")) return ENCMD_Result_Success;

    return ENCMD_Result_Failed;
}

int32_t NrfjprogCMDMgr::NrfjprogReadSNNum(QString& sn)
{
    qDebug() << "[INFO]" << "nrfjprog read sn data";

    sn = "";

    QString memData = "";

    auto ret = nrfjprogMemRead(memData, sn_config_address, sn_config_mem_len);
    if (ret != ENCMD_Result_Success)
    {
        qDebug() << "[ERROR]" << "nrfjprog read mem failed, address: " << sn_config_address << ", read bytes: " << sn_config_mem_len;
        return ENCMD_Result_SN_Read_Failed;
    }

    ret = analyzeMemSnInfo(sn, memData);
    if (ret != ENCMD_Result_Success)
    {
        qDebug() << "[ERROR]" << "analyze memory sn failed";
        return ENCMD_Result_SN_Read_Failed;
    }

    return ENCMD_Result_Success;
}

int32_t NrfjprogCMDMgr::NrfjprogWriteSNNum(QString& sn, const int32_t page)
{
    qDebug() << "[INFO]" << "nrfjprog write sn data";

    QString data = sn.remove(DEL_STR_BLANK);

    if (data.size() != (SN_MAX_LEN + SN_ADD_TAIL_LEN))
    {
        qDebug() << "[ERROR]" << "input sn len error: " << sn;
        return ENCMD_Result_SN_Len_Error;
    }

    int32_t result = ENCMD_Result_Success;
    QString section = "";
    int32_t cur_page = page;

    while (true) {
        section = data.left(4);

        result = nrfjprogMemWrite(section, cur_page);
        if (ENCMD_Result_Success != result)
            break;

        cur_page += 1;
        data.remove(0, 4);

        if (data.size() > 0)
        {
            QEventLoop loop1;
            QTimer::singleShot(500, &loop1, SLOT(quit()));
            loop1.exec();
        }
        else
            break;
    }

    if (ENCMD_Result_Success != result)
    {
        return ENCMD_Result_SN_Write_Section_Failed;
    }
/*
    QString cur_sn;

    if (NrfjprogReadSNNum(cur_sn, page) != ENCMD_Result_Success) return ENCMD_Result_New_SN_Read_Failed;

    if (cur_sn.compare(sn) != 0)
    {
        return ENCMD_Result_New_SN_Read_Not_Match;
    }
*/
    return ENCMD_Result_Success;
}

int32_t NrfjprogCMDMgr::nrfjprogMemRead(QString& ret, const int32_t& mem_address, const int32_t& read_bytes, const int32_t& aligned)
{
    qDebug() << "[INFO]" << "nrfjprog read page mem, mem address: " << mem_address << ", read bytes: " << read_bytes << ", aligend: " << aligned;

    ret = "";

    QString addr = "0x" + QString::number(mem_address, 16);

    qDebug() << "[INFO]" << "page address: " << addr;

    QStringList parameters("--memrd");
    parameters << addr << "--w" << QString::number(aligned) << "--n" << QString::number(read_bytes);

    ret = WinCMDUtil::doCmd(cmd_, parameters);

    qDebug() << "nrfjprog read memory cmd ret info: "<< ret;

    return ret.isEmpty() ? ENCMD_Result_Failed : ENCMD_Result_Success;
}

int32_t NrfjprogCMDMgr::analyzeMemSnInfo(QString& ret, const QString& memData)
{
    qDebug() << "[INFO]" << "analysis mem sn info, mem data: " << memData;

    if (memData.isEmpty())
    {
        return ENCMD_Result_Mem_Data_Empty;
    }

    QStringList lineDataList = memData.split("\r\n");
    if (lineDataList.isEmpty())
    {
        return ENCMD_Result_Analyze_Mem_Data_Failed;
    }

    QRegExp reg_start(REG_START_SIGN), reg_end(REG_END_SIGN);

    for (int i = 0; i < lineDataList.size(); i++)
    {
        QString info = lineDataList[i];
        if (info.isEmpty()) continue;

        int start_index = info.indexOf(reg_start, 0);
        int end_index = info.indexOf(reg_end, 0);

        if (start_index < 0 || end_index < 0)
        {
            qDebug() << "[INFO]" << "one line memory data do not match regexp: " << info;
            continue;
        }

        info.chop(info.size() - end_index);
        info.remove(0, 11);

        if (info.size() > 8)
        {
            QStringList byteInfoList = info.split(" ");

            for (int i = 0; i < byteInfoList.size(); i++)
                ret += getByteDataSn(byteInfoList[i]);
        }
        else
        {
            ret += getByteDataSn(info);
        }
    }

    return ENCMD_Result_Success;
}

QString NrfjprogCMDMgr::getByteDataSn(QString& byteData)
{
    if (byteData.isEmpty()) return "";

    QString data = byteData.remove(DEL_STR_BLANK);

    if (data.size() != 8) return "";

    QString section = "";
    QString ret = "";

    bool isOK;
    int32_t value;

    while (data.size() > 0) {
        section = data.right(2);
        value = section.toInt(&isOK, 16);

        if (section.compare("ff", Qt::CaseInsensitive) != 0 && section.compare("00", Qt::CaseInsensitive) != 0 && isOK)
            ret += (QChar)value;

        data.chop(2);
    }

    return ret;
}

int32_t NrfjprogCMDMgr::nrfjprogMemWrite(const QString& data, const int32_t& page)
{
    qDebug() << "[INFO]" << "nrfjprog write mem data, page: " << page << ", data: " << data;

    if ((page/4) != 0)
    {
        return ENCMD_Result_Page_Parameter_Error;
    }

    if (data.size() > 4)
    {
        return ENCMD_Result_SN_Write_Section_Len_Error;
    }

    QString write_data = data;
    QString section;
    QString hex_data;

    while (write_data.size() > 0) {
        section = write_data.right(1).toLocal8Bit().toHex();

        if (section.size() == 2)
            hex_data += section;
        else
            qDebug() << "[ERROR]" << "convert character to hex error, c: " << write_data.right(1) << ", hex: " << section;

        write_data.chop(1);
    }

    hex_data.prepend("0x");
    QString addr = "0x" + QString::number(sn_config_address + (page*4), 16);

    if (hex_data.isEmpty())
    {
        qDebug() << "[ERROR]" << "write hex data error, hex: " << hex_data;
    }

    qDebug() << "[INFO]" << "page address: " << addr;

    QStringList parameters("--memwr");
    parameters << addr << "--val" << hex_data << "--verify";

    QString ret = WinCMDUtil::doCmd(cmd_, parameters);

    if (!ret.contains("Verified OK."))
    {
        qDebug() << "nrfjprog write memory cmd ret error: "<< ret;
        return ENCMD_Result_SN_Write_Section_Failed;
    }

    return ENCMD_Result_Success;
}
